#include <map>
#include "tree/EmptyStatement.h"
#include "ParseHelper.h"
#include "Exceptions.h"

ParseHelper::ParseHelper()
{
    fMain = 0;
    FillPredefinedFunctions();
}

ParseHelper::~ParseHelper()
{
    for(set<FunctionDefinition*>::iterator it = fAllFunctionDefinitions.begin(); it != fAllFunctionDefinitions.end(); ++it)
    {
        delete (*it);
    }

    for(set<ClassDefinition*>::iterator it = fAllClassDefinitions.begin(); it != fAllClassDefinitions.end(); ++it)
    {
        delete (*it);
    }

    fAllFunctionDefinitions.clear();
    fAllClassDefinitions.clear();

    // ted jeste musime vycistit to, co nam tady zbylo... treba v pripade syntakticke chyby
    while(!fCompStmtListStack.empty())
    {
        vector<TreeNode*> lList = fCompStmtListStack.top();

        for(vector<TreeNode*>::iterator it = lList.begin(); it != lList.end(); ++it)
        {
            delete (*it);
        }

        fCompStmtListStack.pop();
    }

    while(!fCallParamsStack.empty())
    {
        vector<TreeNode*> lList = fCallParamsStack.top();

        for(vector<TreeNode*>::iterator it = lList.begin(); it != lList.end(); ++it)
        {
            delete (*it);
        }

        fCallParamsStack.pop();
    }

    for(vector<MethodDefinition*>::iterator it = fCurrentMethodDefinitions.begin(); it != fCurrentMethodDefinitions.end(); ++it)
    {
        delete (*it);
    }

    for(vector<TreeNode*>::iterator it = fCurrentVarDeclarations.begin(); it != fCurrentVarDeclarations.end(); ++it)
    {
        delete (*it);
    }

    for(vector<TreeNode*>::iterator it = fCurrentVarDeclarations.begin(); it != fCurrentVarDeclarations.end(); ++it)
    {
        delete (*it);
    }

    for(vector<FieldDeclaration*>::iterator it = fCurrentFieldDeclarations.begin(); it != fCurrentFieldDeclarations.end(); ++it)
    {
        delete (*it);
    }
}

void ParseHelper::DoBasicSemanticChecks()
{
    // IDEA: u každé kontroly je to potřeba volat zvlášť na definice funkcí a na definice tříd, potažmo metod
    //       - nedalo by se to nějak sjednotit?
    CheckFunctions();
    CheckClasses();
    ResolveInheritance(); // toto muze byt klidne i niz, ale nutne to musi byt pred CheckVariables
    CheckClassConstructions();
    CheckFunctionCalls();
    CheckVariables();
    CheckExpressionTypes();
}

void ParseHelper::CheckFunctions()
{
    // sem budu ukladat jmeno funkce jako klic a mnozinu poctu parametru jako hodnotu
    map<string, set<int> > lFunctions;

    for(set<FunctionDefinition*>::iterator it = fAllFunctionDefinitions.begin(); it != fAllFunctionDefinitions.end(); ++it)
    {
        string lName = (*it)->GetName();
        int lParamCount = (*it)->GetParamCount();

        if(lFunctions.find(lName) == lFunctions.end())
        {
            // funkci s timhle jmenem jeste neznam
            // TODO: zavola se vubec konstruktor pro set<int>?
            lFunctions[lName].insert(lParamCount);
        }
        else
        {
            // funkci s timhle jmenem uz mam definovanou... opatrne...
            if(lFunctions[lName].find(lParamCount) != lFunctions[lName].end())
            {
                // taaak a mam te
                throw AmbiguousFunctionDefinitionException(lName, lParamCount);
            }
            else
                lFunctions[lName].insert(lParamCount);
        }

        if(lName == "main")
            fMain = *it;
    }

    if(fMain == 0)
        throw MainFunctionNotDefinedException();
}

void ParseHelper::CheckClasses()
{
    set<string> lClassNames;

    for(set<ClassDefinition*>::iterator it = fAllClassDefinitions.begin(); it != fAllClassDefinitions.end(); ++it)
    {
        string lName = (*it)->GetClassName();

        if(lClassNames.find(lName) != lClassNames.end())
            throw AmbiguousClassDefinitionException(lName);

        lClassNames.insert(lName);
    }

    lClassNames.clear();

    for(set<ClassDefinition*>::iterator it = fAllClassDefinitions.begin(); it != fAllClassDefinitions.end(); ++it)
    {
        (*it)->CheckMethods();
    }
}

void ParseHelper::CheckClassConstructions()
{
    set<FunctionDefinition*>::iterator lIteratorFunctions;
    set<FunctionDefinition*>::iterator lEndFn = fAllFunctionDefinitions.end();
    set<ClassDefinition*>::iterator lIteratorClasses;
    set<ClassDefinition*>::iterator lEndCls = fAllClassDefinitions.end();

    for(lIteratorFunctions = fAllFunctionDefinitions.begin(); lIteratorFunctions != lEndFn; ++lIteratorFunctions)
    {
        (*lIteratorFunctions)->ResolveClassConstructions(fAllClassDefinitions);
    }

    for(lIteratorClasses = fAllClassDefinitions.begin(); lIteratorClasses != lEndCls; ++lIteratorClasses)
    {
        (*lIteratorClasses)->ResolveClassConstructions(fAllClassDefinitions);
    }
}

void ParseHelper::CheckFunctionCalls()
{
    set<FunctionDefinition*>::iterator lIteratorFunctions;
    set<FunctionDefinition*>::iterator lEndFn = fAllFunctionDefinitions.end();
    set<ClassDefinition*>::iterator lIteratorClasses;
    set<ClassDefinition*>::iterator lEndCls = fAllClassDefinitions.end();

    for(lIteratorFunctions = fAllFunctionDefinitions.begin(); lIteratorFunctions != lEndFn; ++lIteratorFunctions)
    {
        (*lIteratorFunctions)->ResolveFunctionCalls(fAllFunctionDefinitions);
    }

    for(lIteratorClasses = fAllClassDefinitions.begin(); lIteratorClasses != lEndCls; ++lIteratorClasses)
    {
        (*lIteratorClasses)->ResolveFunctionCalls(fAllFunctionDefinitions);
    }
}

void ParseHelper::CheckVariables()
{
    set<FunctionDefinition*>::iterator lIteratorFunctions;
    set<FunctionDefinition*>::iterator lEndFn = fAllFunctionDefinitions.end();
    set<ClassDefinition*>::iterator lIteratorClasses;
    set<ClassDefinition*>::iterator lEndCls = fAllClassDefinitions.end();

    for(lIteratorFunctions = fAllFunctionDefinitions.begin(); lIteratorFunctions != lEndFn; ++lIteratorFunctions)
    {
        (*lIteratorFunctions)->ResolveVariables();
    }

    for(lIteratorClasses = fAllClassDefinitions.begin(); lIteratorClasses != lEndCls; ++lIteratorClasses)
    {
        (*lIteratorClasses)->ResolveVariables();
    }
}

void ParseHelper::CheckExpressionTypes()
{
    set<FunctionDefinition*>::iterator lIteratorFunctions;
    set<FunctionDefinition*>::iterator lEndFn = fAllFunctionDefinitions.end();
    set<ClassDefinition*>::iterator lIteratorClasses;
    set<ClassDefinition*>::iterator lEndCls = fAllClassDefinitions.end();

    for(lIteratorFunctions = fAllFunctionDefinitions.begin(); lIteratorFunctions != lEndFn; ++lIteratorFunctions)
    {
        (*lIteratorFunctions)->CheckExpressionTypes();
    }

    for(lIteratorClasses = fAllClassDefinitions.begin(); lIteratorClasses != lEndCls; ++lIteratorClasses)
    {
        (*lIteratorClasses)->CheckExpressionTypes();
    }
}

void ParseHelper::ResolveInheritance()
{
    set<ClassDefinition*>::iterator lIteratorClasses;
    set<ClassDefinition*>::iterator lEndCls = fAllClassDefinitions.end();

    for(lIteratorClasses = fAllClassDefinitions.begin(); lIteratorClasses != lEndCls; ++lIteratorClasses)
    {
        (*lIteratorClasses)->ResolveInheritance(fAllClassDefinitions);
    }
}

void ParseHelper::FillPredefinedFunctions()
{
    vector<Variable*> lInputVars;
    string lFName;

    lInputVars.push_back(new Variable("a"));
    lFName = Util::BuiltInFunctionType2String(PrintFunction);
    FunctionDefinition* lFun = new FunctionDefinition(lFName, lInputVars, new EmptyStatement(), true);
    lFun->SetBuiltInFunctionType(PrintFunction);

    this->fAllFunctionDefinitions.insert(lFun);

    lInputVars.clear();
    lInputVars.push_back(new Variable("a"));
    lFName = Util::BuiltInFunctionType2String(ArrayFunction);
    lFun = new FunctionDefinition(lFName, lInputVars, new EmptyStatement(), true);
    lFun->SetBuiltInFunctionType(ArrayFunction);

    this->fAllFunctionDefinitions.insert(lFun);
}

void ParseHelper::NewCallContext()
{
	fCallParamsStack.push(vector<TreeNode*>());
}

void ParseHelper::NewCompStmtContext()
{
    fCompStmtListStack.push(vector<TreeNode*>());
}

void ParseHelper::AddDefinitionParameter( Variable * v )
{
	fCurrentParameterDeclarations.push_back(v);
}

void ParseHelper::AddCallParam( TreeNode * e )
{
	fCallParamsStack.top().push_back(e);
}

void ParseHelper::AddVar( Statement * v )
{
	fCurrentVarDeclarations.push_back(v);
}

void ParseHelper::AddBaseClass( string s )
{
	fCurrentParentClasses.push_back(s);
}

void ParseHelper::AddFunction( FunctionDefinition * f )
{
	fAllFunctionDefinitions.insert(f);
}

void ParseHelper::AddMethod( MethodDefinition * m )
{
	fCurrentMethodDefinitions.push_back(m);
}

void ParseHelper::AddField( string s )
{
	fCurrentFieldDeclarations.push_back(new FieldDeclaration(s));
}

void ParseHelper::AddClass( ClassDefinition * c )
{
	fAllClassDefinitions.insert(c);
}

void ParseHelper::AddStatement( TreeNode * s )
{
	fCompStmtListStack.top().push_back(s);
}

vector<TreeNode*> ParseHelper::GetCallParams()
{
	vector<TreeNode*> ret(fCallParamsStack.top());
	fCallParamsStack.pop();
	return ret;
}

vector<TreeNode*> ParseHelper::GetVarDeclarations()
{
	vector<TreeNode*> ret(fCurrentVarDeclarations);
	fCurrentVarDeclarations.clear();
	return ret;
}

vector<Variable*> ParseHelper::GetDefinitonParams()
{
	vector<Variable*> ret(fCurrentParameterDeclarations);
	fCurrentParameterDeclarations.clear();
	return ret;
}

vector<TreeNode*> ParseHelper::GetStatements()
{
    vector<TreeNode*> ret(fCompStmtListStack.top());
    fCompStmtListStack.pop();
	return ret;
}

vector<FieldDeclaration*> ParseHelper::GetFields()
{
	vector<FieldDeclaration*> ret(fCurrentFieldDeclarations);
	fCurrentFieldDeclarations.clear();
	return ret;
}

vector<MethodDefinition*> ParseHelper::GetMethods()
{
	vector<MethodDefinition*> ret(fCurrentMethodDefinitions);
	fCurrentMethodDefinitions.clear();
	return ret;
}

vector<string> ParseHelper::GetBaseClasses()
{
	vector<string> ret(fCurrentParentClasses);
	fCurrentParentClasses.clear();
	return ret;
}

void ParseHelper::SanitizeString(string** oInput)
{
    string lVal = **oInput;

    if(lVal.substr(0, 1) == "\"" || lVal.substr(lVal.length() - 1) == "\"")
    {
        string::iterator lItBegin = lVal.begin();
        string::iterator lItEnd = lVal.end();

        if(lVal.substr(0, 1) == "\"")
            lItBegin++;

        if(lVal.substr(lVal.length() - 1) == "\"")
            lItEnd--;

        string* lNewValue = new string(lItBegin, lItEnd);
        delete (*oInput);
        *oInput = lNewValue;
    }
}
